/**
 * BleConnection class
 */
class BleConnection {

  properties = {
    deviceId: null,
    mtuSize: 23
  }
  constructor() {
    // onConnectionStateChanged(this.info)
  }
  /**
   * @param deviceId Android:Mac;IOS:UUID
   */
  async connect(deviceId) {
    this.properties.deviceId = deviceId
    //todo 判断是否已经链接
    await connectDevice(deviceId)
    return await this.setup(deviceId)
  }

  async setup(deviceId) {
    const services = await discoverServices(deviceId)
    return discoverCharacters(deviceId, services)
  }

  read(serviceId, characteristicId) {
    let that = this
    return new Promise((resolve, reject) => {
      wx.readBLECharacteristicValue({
        characteristicId: characteristicId,
        deviceId: that.properties.deviceId,
        serviceId: serviceId,
        success(res) {
          resolve(res)
        },
        fail(err) {
          reject(err)
        }
      })
    })
  }

  /**
   * @param {*} needResponse write/writeNoResponse
   */
  writeBuffer(serviceId, characteristicId, needResponse, arrayBuffer) {
    let that = this
    let type
    if (needResponse) {
      type = 'write'
    } else {
      type = 'writeNoResponse'
    }
    return new Promise((resolve, reject) => {

      let timeoutId = setTimeout(function(){
        reject("wx.writeBLECharacteristicValue Timeout")
      },3000)

      wx.writeBLECharacteristicValue({
        deviceId: that.properties.deviceId,
        serviceId: serviceId,
        characteristicId: characteristicId,
        value: arrayBuffer,
        writeType: type,
        success(res) {
          clearTimeout(timeoutId)
          resolve(res)
        },
        fail(err) {
          clearTimeout(timeoutId)
          console.error("Error write:"+err)
          console.log(arrayBuffer)
          reject(err)
        }
      })
    })
  }
  /**
   * @param {*} needResponse indicate/notification
   * 监听蓝牙低功耗设备的特征值变化事件。必须先调用 wx.onBLECharacteristicValueChange 接口才能接* 收到设备推送的 notification。
   */
  enableNotification(serviceId, characteristicId, needResponse) {
    let that = this
    let type
    if (needResponse) {
      type = 'indicate'
    } else {
      type = 'notification'
    }
    return new Promise((resolve, reject) => {
      wx.notifyBLECharacteristicValueChange({
        deviceId: that.properties.deviceId,
        serviceId: serviceId,
        characteristicId: characteristicId,
        state: true,
        type: type,
        success(res) {
          console.log(res)
          resolve(res)
        },
        fail(err) {
          console.log(err)
          reject(err)
        }
      })
    })
  }
  requestMtu(mtuSize) {
    let that = this
    return new Promise((resolve, reject) => {
      wx.setBLEMTU({
        deviceId: that.properties.deviceId,
        mtu: mtuSize,
        complete(res) {
          console.log(res)
          wx.getBLEMTU({
            deviceId: that.properties.deviceId,
            success(res) {
              console.log(res)
              that.properties.mtuSize = res.mtu
              resolve(res)
            },
            fail(res) {
              console.error(res)
              reject(res)
            }
          })
        }
      })
    })
  }

  disconnect() {
    let that = this
    wx.closeBLEConnection({
      deviceId: that.properties.deviceId,
      success(res) {
        console.log(res);
      },
      fail(res) {
        console.log(res);
      }
    })
  }

}

/**
 * 开始连接设备
 * @param {*} properties 
 */
function connectDevice(deviceId) {
  return new Promise((resolve, reject) => {
    wx.createBLEConnection({
      deviceId: deviceId,
      timeout: 10 * 1000,
      success(res) {
        console.log(res)
        resolve(res)
      },
      fail(res) {
        reject(res)
      }
    })
  })
}

function discoverServices(deviceId) {
  return new Promise((resolve, reject) => {
    wx.getBLEDeviceServices({
      //保存设备Mac地址或者UUID
      deviceId: deviceId,
      success: (res) => {
        //保存服务
        resolve(res.services)
      },
      fail(res) {
        reject(res)
      }
    })
  })
}

/**
 *  promise数据的遍历
 * Promise.all(promises),\\顺序，按照请求顺序排序
 * Promise.race(promises)\\竞速，按照回复先后排序
 */
function discoverCharacters(deviceId, services) {
  let promises = new Array(services.length)
  for (let i = 0; i < services.length; i++) {
    promises[i] = discoverCharacter(deviceId, services[i].uuid)
  }
  return Promise.all(promises)
}
//这里有一个错误，不会执行reject，否则会打断队列任务，副作用是可能有些错误的服务没有特征
function discoverCharacter(deviceId, serviceId) {
  return new Promise((resolve, reject) => {
    wx.getBLEDeviceCharacteristics({
      deviceId,
      serviceId,
      success: (res) => {
        resolve(res)
      },
      fail(err) {
        resolve(err)
        // reject(err)
      }
    })
  })
}

function buf2hex(buffer) { // buffer is an ArrayBuffer
  return [...new Uint8Array(buffer)]
    .map(x => x.toString(16).padStart(2, '0'))
    .join(' ');
}
export {
  BleConnection
}